package se581.banker.proxy;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

import org.apache.log4j.Logger;

import se581.banker.SimulationClientMain;
import se581.banker.event.TrackerEventWrapper;
import se581.banker.manager.AccountTracker;
import se581.banker.manager.TrackerObserver;
import se581.banker.util.ServerSocketInfo;

/**
 * Proxy to abstract the query client from the InquiryTracker  
 * communication logic
 * 
 * @author mmichalak
 *
 */
public class InquiryProxy implements TrackerProxy, TrackerObserver {
	static Logger logger = Logger.getLogger(InquiryProxy.class.getName());
	int MAX_NUM_SOCKET_RETRIES = 2;

	String myHost = null;
	String myPort = null;


	/*
	 * This is the server info used by this proxy
	 */
	ServerSocketInfo serverSocketInfo = null;
	int openSocketFails = 0;
	
	
	public InquiryProxy(String myHost, String myPort) {
		super();
		this.myHost = myHost;
		this.myPort = myPort;
	}
	

	public void processRequest(TrackerEventWrapper evWrapper) {
		logger.debug("Beginning process");
		
		if (evWrapper == null) {
			logger.error("ERROR - TrackerEventWrapper is null");
			return;
		}
		else if (evWrapper.getEvent() == null) {
			logger.error("TrackerEventWrapper is null or the TrackerEvent is null:  " + evWrapper.toString());
			evWrapper.setServerSuccess(false);
			if (evWrapper != null)
				evWrapper.setServerReturn("ERROR - Event is missing from EventWrapper.");
			return;
		}

		if (serverSocketInfo == null || openSocketFails == MAX_NUM_SOCKET_RETRIES)
			registerWithRegistrationSvc(myHost, myPort); // sets to class var serverSocketInfo

		
	  String socketHostPort = null;
	  Socket clientSocket = null;
	  DataOutputStream outToServer = null;
	  BufferedReader inFromServer = null;
	  	try {
			clientSocket = new Socket( serverSocketInfo.getHost(), Integer.parseInt(serverSocketInfo.getPort()) );
			
			outToServer =
			     new DataOutputStream(
			     clientSocket.getOutputStream());
			
			inFromServer =
			     new BufferedReader(
			     new InputStreamReader(
			     clientSocket.getInputStream()));
		  
		} catch (Exception e) {
			logger.error("Error opening client socket to localhost port " + socketHostPort);
			e.printStackTrace(); // May comment out at some point
			evWrapper.setServerSuccess(false);
			evWrapper.setServerReturn("Error establishing connection to server - " + e.getMessage());

			// Error here, close socket
			try { clientSocket.close(); } catch (IOException ex) {
				logger.error("Error closing socket in finally....Oooops, socket leak");
			}
			openSocketFails++;
		}
	
        
	      String retFromServer = "Error - No Response from Server";
	      try {
			  outToServer.writeBytes( evWrapper.getEvent().toEventLine()  + '\n'); // Checked for null above
			  retFromServer = inFromServer.readLine();
			  
			  logger.debug("FROM SERVER: " + retFromServer);
			  evWrapper.setServerReturn(retFromServer);

		} catch (Exception e) {
			evWrapper.setServerSuccess(false);
			evWrapper.setServerReturn("Error communicating with server: " + socketHostPort);
			openSocketFails++;
			//e.printStackTrace();
		} finally {
			try { clientSocket.close(); } catch (IOException e) {
				logger.error("Error closing socket in finally....Oooops, socket leak");
			}
		}
     
		return;
	}
	
	
	/**
	 * Returns the socket HOST:PORT for UPDATE server communication
	 * 
	 * Request:  INQUIRY,localhost:8888
	 * Response:
	 * 
	 * @param host
	 * @param port
	 */
	void registerWithRegistrationSvc(String myHost, String myPort) {
		logger.debug("Registering INQUIRY with RegistrationService....");

		if (myHost == null || myHost.isEmpty() || myPort == null || myPort.isEmpty())
			logger.error("ERROR registering with Update Server.  myHost and/or myPort is null or empyty.  myHost:myPort = " + myHost + ":" + myPort);
		
		openSocketFails = 0; // reset

		String updateSvrHostPort = null;
       
	      try {
			Socket clientSocket = new Socket( 
									SimulationClientMain.registrationServerInfo.getHost(),
									Integer.parseInt(SimulationClientMain.registrationServerInfo.getPort()) );
				  
			  DataOutputStream outToServer =
			     new DataOutputStream(
			     clientSocket.getOutputStream());

			  BufferedReader inFromServer =
			     new BufferedReader(
			     new InputStreamReader(
			     clientSocket.getInputStream()));

			  outToServer.writeBytes("INQUIRY," + myHost + ":" + myPort + '\n');

			  String updateSvrInfoString = inFromServer.readLine();
			  serverSocketInfo = new ServerSocketInfo(updateSvrInfoString);
			  logger.debug("FROM SERVER: " + updateSvrHostPort);
			
			  clientSocket.close();
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * AccountTracker Failover observer callback method
	 */
	public void updateTracker(AccountTracker accountTracker) {
//		bTracker = accountTracker;
	}

}
